Class pv.Mark
Represents a data-driven graphical mark. The Mark class is the base class for all graphical marks in Protovis; it does not provide any specific rendering functionality, but together with Panel establishes the core framework.
Concrete mark types include familiar visual elements such as bars, lines and labels. Although a bar mark may be used to construct a bar chart, marks know nothing about charts; it is only through their specification and composition that charts are produced. These building blocks permit many combinatorial possibilities.
Marks are associated with data: a mark is generated once per associated datum, mapping the datum to visual properties such as position and color. Thus, a single mark specification represents a set of visual elements that share the same data and visual encoding. The type of mark defines the names of properties and their meaning. A property may be static, ignoring the associated datum and returning a constant; or, it may be dynamic, derived from the associated datum or index. Such dynamic encodings can be specified succinctly using anonymous functions. Special properties called event handlers can be registered to add interactivity.
Protovis uses inheritance to simplify the specification of related marks: a new mark can be derived from an existing mark, inheriting its properties. The new mark can then override properties to specify new behavior, potentially in terms of the old behavior. In this way, the old mark serves as the prototype for the new mark. Most mark types share the same basic properties for consistency and to facilitate inheritance.
The prioritization of redundant properties is as follows:
- If the width property is not specified (i.e., null), its value is the width of the parent panel, minus this mark's left and right margins; the left and right margins are zero if not specified.
- Otherwise, if the right margin is not specified, its value is the width of the parent panel, minus this mark's width and left margin; the left margin is zero if not specified.
- Otherwise, if the left property is not specified, its value is the width of the parent panel, minus this mark's width and the right margin.
While most properties are variable, some mark types, such as lines and areas, generate a single visual element rather than a distinct visual element per datum. With these marks, some properties may be fixed. Fixed properties can vary per mark, but not per datum! These properties are evaluated solely for the first (0-index) datum, and typically are specified as a constant. However, it is valid to use a function if the property varies between panels or is dynamically generated.
See also the Protovis guide.
Defined in: Mark.js.
Constructor Attributes | Constructor Name and Description |
---|---|
pv.Mark()
Constructs a new mark with default properties.
|
Field Attributes | Field Name and Description |
---|---|
The bottom margin; the distance, in pixels, between the bottom edge of the
enclosing panel and the bottom edge of this mark.
|
|
The child index.
|
|
The cursor property; corresponds to the CSS cursor property.
|
|
The data property; an array of objects.
|
|
Default properties for all mark types.
|
|
The mark index.
|
|
The left margin; the distance, in pixels, between the left edge of the
enclosing panel and the left edge of this mark.
|
|
The enclosing parent panel.
|
|
The mark prototype, possibly undefined, from which to inherit property
functions.
|
|
The reverse property; a boolean determining whether marks are ordered from
front-to-back or back-to-front.
|
|
The right margin; the distance, in pixels, between the right edge of the
enclosing panel and the right edge of this mark.
|
|
The root parent panel.
|
|
The scene graph.
|
|
The title property; corresponds to the HTML/SVG title property, allowing the
general of simple plain text tooltips.
|
|
The top margin; the distance, in pixels, between the top edge of the
enclosing panel and the top edge of this mark.
|
|
The mark type; a lower camelCase name.
|
|
The visible property; a boolean determining whether or not the mark instance
is visible.
|
Method Attributes | Method Name and Description |
---|---|
add(type)
Adds a new mark of the specified type to the enclosing parent panel, whilst
simultaneously setting the prototype of the new mark to be this mark.
|
|
anchor(name)
Returns an anchor with the specified name.
|
|
Returns the anchor target of this mark, if it is derived from an anchor;
otherwise returns null.
|
|
cousin()
Returns the current instance in the scene graph of this mark, in the previous
instance of the enclosing parent panel.
|
|
def(name, value)
Defines a local variable on this mark.
|
|
event(type, handler)
Registers an event handler for the specified event type with this mark.
|
|
extend(proto)
Sets the prototype of this mark to the specified mark.
|
|
first()
Returns the first instance of this mark in the scene graph.
|
|
last()
Returns the last instance of this mark in the scene graph.
|
|
mouse()
Returns the current location of the mouse (cursor) relative to this mark's
parent.
|
|
render()
Renders this mark, including recursively rendering all child marks if this is
a panel.
|
|
sibling()
Returns the previous instance of this mark in the scene graph, or null if
this is the first instance.
|
- See:
- CSS2 cursor
m.data([1, 2, 3, 4, 5]);However, it is perfectly acceptable to define the data property as a function. This function might compute the data dynamically, allowing different data to be used per enclosing panel. For instance, in the stacked area graph example (see #scene), the data function on the area mark dereferences each series.
For instance, consider a stacked area chart. The bottom property of the area can be defined using the cousin instance, which is the current area instance in the previous instantiation of the parent panel. In this sample code,
new pv.Panel() .width(150).height(150) .add(pv.Panel) .data([[1, 1.2, 1.7, 1.5, 1.7], [.5, 1, .8, 1.1, 1.3], [.2, .5, .8, .9, 1]]) .add(pv.Area) .data(function(d) d) .bottom(function() { var c = this.cousin(); return c ? (c.bottom + c.height) : 0; }) .height(function(d) d * 40) .left(function() this.index * 35) .root.render();the bottom property is computed based on the upper edge of the corresponding datum in the previous series. The area's parent panel is instantiated once per series, so the cousin refers to the previous (below) area mark. (Note that the position of the upper edge is not the same as the top property, which refers to the top margin: the distance from the top edge of the panel to the top edge of the mark.)
- Parameters:
- {function} type
- the type of mark to add; a constructor, such as pv.Bar.
- Returns:
- {pv.Mark} the new mark.
- See:
- #extend
- Parameters:
- {string} name
- the anchor name; either a string or a property function.
- Returns:
- {pv.Anchor} the new anchor.
bar.anchor("top").add(pv.Label);then property functions on the label can refer to the bar via the anchorTarget method. This method is also useful for mark types defining properties on custom anchors.
- Returns:
- {pv.Mark} the anchor target of this mark; possibly null.
- Returns:
- a node in the scene graph, or null.
- See:
- pv.Layout.stack
1. To store local state. For example, say you were visualizing employment statistics, and your root panel had an array of occupations. In a child panel, you might want to initialize a local scale, and reference it from a property function:
.def("y", function(d) pv.Scale.linear(0, pv.max(d.values)).range(0, h)) .height(function(d) this.y()(d))In this example, this.y() returns the defined local scale. We then invoke the scale function, passing in the datum, to compute the height. Note that defs are similar to fixed properties: they are only evaluated once per parent panel, and this.y() returns a function, rather than automatically evaluating this function as a property.
2. To store temporary state for interaction. Say you have an array of bars, and you want to color the bar differently if the mouse is over it. Use def to define a local variable, and event handlers to override this variable interactively:
.def("i", -1) .event("mouseover", function() this.i(this.index)) .event("mouseout", function() this.i(-1)) .fillStyle(function() this.i() == this.index ? "red" : "blue")Notice that this.i() can be used both to set the value of i (when an argument is specified), and to get the value of i (when no arguments are specified). In this way, it's like other property methods.
3. To specify fixed properties efficiently. Sometimes, the value of a property may be locally a constant, but dependent on parent panel data which is variable. In this scenario, you can use def to define a property; it will only get computed once per mark, rather than once per datum.
- Parameters:
- {string} name
- the name of the local variable.
- {function} value Optional
- an optional initializer; may be a constant or a function.
m.event("click", function() this.fillStyle("red"));Alternatively, the external data can be manipulated and the visualization redrawn:
m.event("click", function(d) { data = all.filter(function(k) k.name == d); vis.render(); });The return value of the event handler determines which mark gets re-rendered. Use defs (#def) to set temporary state from event handlers.
The complete set of event types is defined by SVG; see the reference below. The set of supported event types is:
- click
- mousedown
- mouseup
- mouseover
- mousemove
- mouseout
TODO In the current implementation, event handlers are not inherited from prototype marks. They must be defined explicitly on each interactive mark. In addition, only one event handler for a given event type can be defined; when specifying multiple event handlers for the same type, only the last one will be used.
- Parameters:
- {string} type
- the event type.
- {function} handler
- the event handler.
- Returns:
- {pv.Mark} this.
- See:
- SVG events
- Parameters:
- {pv.Mark} proto
- the new prototype.
- Returns:
- {pv.Mark} this mark.
- See:
- #add
- Returns:
- a node in the scene graph.
- Returns:
- a node in the scene graph.
- Returns:
- {pv.Vector} the mouse location.
- Returns:
- a node in the scene graph, or null.